package com.piece.core.session.strategy;

import com.piece.core.framework.util.basic.I18nUtil;
import com.piece.core.framework.constant.ExceptionAuthConstants;
import com.piece.core.framework.support.dto.UserDetailDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.session.SessionInformation;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.web.authentication.session.SessionAuthenticationException;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.util.Assert;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Iterator;
import java.util.List;

/**
 * 并发登陆控制
 */
@Slf4j
public class DefaultConcurrentSessionControlAuthenticationStrategy implements MessageSourceAware, SessionAuthenticationStrategy {

    protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
    private final SessionRegistry sessionRegistry;
    private boolean exceptionIfMaximumExceeded = false;
    private int maximumSessions = 1;

    public DefaultConcurrentSessionControlAuthenticationStrategy(SessionRegistry sessionRegistry, boolean exceptionIfMaximumExceeded) {
        this.sessionRegistry = sessionRegistry;
        this.exceptionIfMaximumExceeded = exceptionIfMaximumExceeded;
    }

    public void onAuthentication(Authentication authentication, HttpServletRequest request, HttpServletResponse response) {
        this.authenticationSession(authentication, request, response);
    }

    private void authenticationSession(Authentication authentication, HttpServletRequest request, HttpServletResponse response) {
        UserDetailDTO user = (UserDetailDTO) authentication.getPrincipal();
        List<SessionInformation> sessions = this.sessionRegistry.getAllSessions(user.getLoginName(), false);
        int sessionCount = sessions.size();
        int allowedSessions = this.getMaximumSessionsForThisUser(authentication);
        if (sessionCount >= allowedSessions) {
            if (allowedSessions != -1) {
                if (sessionCount == allowedSessions) {
                    HttpSession session = request.getSession(false);
                    if (null != session) {
                        Iterator var8 = sessions.iterator();

                        while(var8.hasNext()) {
                            SessionInformation si = (SessionInformation)var8.next();
                            if (si.getSessionId().equals(session.getId())) {
                                return;
                            }
                        }
                    }
                }
                this.allowableSessionsExceeded(sessions, allowedSessions, this.sessionRegistry);
            }
        }
    }

    protected int getMaximumSessionsForThisUser(Authentication authentication) {
        return this.maximumSessions;
    }

    protected void allowableSessionsExceeded(List<SessionInformation> sessions, int allowableSessions, SessionRegistry registry) throws SessionAuthenticationException {
        if (this.exceptionIfMaximumExceeded && sessions != null) {
            SessionInformation leastRecentlyUsed = null;
            Iterator var5 = sessions.iterator();

            while(true) {
                SessionInformation session;
                do {
                    if (!var5.hasNext()) {
                        leastRecentlyUsed.expireNow();
                        return;
                    }

                    session = (SessionInformation)var5.next();
                } while(null != leastRecentlyUsed && !session.getLastRequest().before(leastRecentlyUsed.getLastRequest()));

                leastRecentlyUsed = session;
            }
        } else {
            throw new SessionAuthenticationException(I18nUtil.message(ExceptionAuthConstants.LOGIN_MAX));
        }
    }

    public void setExceptionIfMaximumExceeded(boolean exceptionIfMaximumExceeded) {
        this.exceptionIfMaximumExceeded = exceptionIfMaximumExceeded;
    }

    public void setMaximumSessions(int maximumSessions) {
        Assert.isTrue(maximumSessions != 0, "MaximumLogins must be either -1 to allow unlimited logins, or a positive integer to specify a maximum");
        this.maximumSessions = maximumSessions;
    }

    public void setMessageSource(MessageSource messageSource) {
        Assert.notNull(messageSource, "messageSource cannot be null");
        this.messages = new MessageSourceAccessor(messageSource);
    }
}
